/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ant.internal.ui.views.actions; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.ant.internal.core.IAntCoreConstants; import org.eclipse.ant.internal.ui.AntUIPlugin; import org.eclipse.ant.internal.ui.IAntUIHelpContextIds; import org.eclipse.ant.internal.ui.IAntUIPreferenceConstants; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceProxy; import org.eclipse.core.resources.IResourceProxyVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.IWorkingSetSelectionDialog; /** * This dialog allows the user to search for Ant build files whose names match a given pattern. The search may be performed on the entire workspace or * it can be limited to a particular working set. */ public class SearchForBuildFilesDialog extends InputDialog { /** * List of <code>IFile</code> objects that were found */ private List<IResource> results = new ArrayList<>(); /** * List of <code>IResource</code> objects in which to search. * * If the searchScopes are <code>null</code>, the user has asked to search the workspace. If the searchScopes are empty, the user has asked to * search a working set that has no resources. */ private List<IResource> searchScopes = null; /** * The working set scope radio button. */ private Button workingSetScopeButton; /** * The workspace scope radio button. */ private Button workspaceScopeButton; /** * The text field that displays the current working set name */ private Text workingSetText; /** * The button that allows the user to decide if error results should be parsed */ private Button includeErrorResultButton; /** * The dialog settings used to persist this dialog's settings. */ private static IDialogSettings settings = AntUIPlugin.getDefault().getDialogSettings(); /** * Initialize any dialog settings that haven't been set. */ static { if (settings.get(IAntUIPreferenceConstants.ANTVIEW_LAST_SEARCH_STRING) == null) { settings.put(IAntUIPreferenceConstants.ANTVIEW_LAST_SEARCH_STRING, "build.xml"); //$NON-NLS-1$ } if (settings.get(IAntUIPreferenceConstants.ANTVIEW_LAST_WORKINGSET_SEARCH_SCOPE) == null) { settings.put(IAntUIPreferenceConstants.ANTVIEW_LAST_WORKINGSET_SEARCH_SCOPE, IAntCoreConstants.EMPTY_STRING); } } /** * Creates a new dialog to search for build files. */ public SearchForBuildFilesDialog() { super(Display.getCurrent().getActiveShell(), AntViewActionMessages.SearchForBuildFilesDialog_Search_for_Build_Files_1, AntViewActionMessages.SearchForBuildFilesDialog__Input, settings.get(IAntUIPreferenceConstants.ANTVIEW_LAST_SEARCH_STRING), new IInputValidator() { @Override public String isValid(String newText) { String trimmedText = newText.trim(); if (trimmedText.length() == 0) { return AntViewActionMessages.SearchForBuildFilesDialog_Build_name_cannot_be_empty_3; } return null; } }); } /** * Change the label on the "Ok" button and initialize the enabled state */ @Override protected void createButtonsForButtonBar(Composite parent) { super.createButtonsForButtonBar(parent); getOkButton().setText(AntViewActionMessages.SearchForBuildFilesDialog__Search_4); String workingSetName = settings.get(IAntUIPreferenceConstants.ANTVIEW_LAST_WORKINGSET_SEARCH_SCOPE); if (workingSetName.length() > 0) { setWorkingSet(PlatformUI.getWorkbench().getWorkingSetManager().getWorkingSet(workingSetName)); } if (!settings.getBoolean(IAntUIPreferenceConstants.ANTVIEW_USE_WORKINGSET_SEARCH_SCOPE)) { selectRadioButton(workspaceScopeButton); handleRadioButtonPressed(); } } /** * Add the scope selection widgets to the dialog area */ @Override protected Control createDialogArea(Composite parent) { Font font = parent.getFont(); Composite composite = (Composite) super.createDialogArea(parent); createIncludeErrorResultButton(composite, font); createScopeGroup(composite, font); return composite; } private void createScopeGroup(Composite composite, Font font) { Group scope = new Group(composite, SWT.NONE); scope.setText(AntViewActionMessages.SearchForBuildFilesDialog_Scope_5); GridData data = new GridData(GridData.FILL_BOTH); scope.setLayoutData(data); GridLayout layout = new GridLayout(3, false); scope.setLayout(layout); scope.setFont(font); // Create a composite for the radio buttons Composite radioComposite = new Composite(scope, SWT.NONE); GridLayout radioLayout = new GridLayout(); radioLayout.marginHeight = 0; radioComposite.setLayout(radioLayout); SelectionAdapter selectionListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { handleRadioButtonPressed(); } }; workspaceScopeButton = new Button(radioComposite, SWT.RADIO); workspaceScopeButton.setFont(font); workspaceScopeButton.setText(AntViewActionMessages.SearchForBuildFilesDialog__Workspace_6); workspaceScopeButton.addSelectionListener(selectionListener); workingSetScopeButton = new Button(radioComposite, SWT.RADIO); workingSetScopeButton.setFont(font); workingSetScopeButton.setText(AntViewActionMessages.SearchForBuildFilesDialog_Wor_king_Set__7); workingSetScopeButton.addSelectionListener(selectionListener); selectRadioButton(workspaceScopeButton); workingSetText = new Text(scope, SWT.BORDER); workingSetText.setEditable(false); data = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_END); workingSetText.setLayoutData(data); workingSetText.setFont(font); Button chooseButton = new Button(scope, SWT.PUSH); data = new GridData(GridData.VERTICAL_ALIGN_END); chooseButton.setLayoutData(data); chooseButton.setFont(font); chooseButton.setText(AntViewActionMessages.SearchForBuildFilesDialog__Choose____8); chooseButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent evt) { handleChooseButtonPressed(); } }); } /** * Programatically selects the given radio button, deselecting the other radio button. * * @param button * the radio button to select. This parameter must be one of either the <code>workingSetScopeButton</code> or the * <code>workspaceScopeButton</code> or this method will have no effect. */ private void selectRadioButton(Button button) { if (button == workingSetScopeButton) { workingSetScopeButton.setSelection(true); workspaceScopeButton.setSelection(false); } else if (button == workspaceScopeButton) { workspaceScopeButton.setSelection(true); workingSetScopeButton.setSelection(false); } } /** * One of the search scope radio buttons has been pressed. Update the dialog accordingly. */ private void handleRadioButtonPressed() { if (workingSetScopeButton.getSelection()) { IWorkingSet set = PlatformUI.getWorkbench().getWorkingSetManager().getWorkingSet(getWorkingSetName()); if (set != null) { setWorkingSet(set); return; } } setWorkingSet(null); } /** * Returns the working set name currently displayed. */ private String getWorkingSetName() { return workingSetText.getText().trim(); } /** * Creates the button that allows the user to specify whether or not build files should that cannot be parsed should be included in the results. */ private void createIncludeErrorResultButton(Composite composite, Font font) { includeErrorResultButton = new Button(composite, SWT.CHECK); includeErrorResultButton.setFont(font); includeErrorResultButton.setText(AntViewActionMessages.SearchForBuildFilesDialog_Include_errors); includeErrorResultButton.setSelection(settings.getBoolean(IAntUIPreferenceConstants.ANTVIEW_INCLUDE_ERROR_SEARCH_RESULTS)); } /** * Updates the dialog based on the state of the working set settings * <ul> * <li>Sets the enablement of the "Search" button based on the validity of the settings</li> * <li>Sets any or clears the error message</li> * </ul> */ private void updateForWorkingSetSettings() { if (workingSetScopeButton.getSelection()) { String error = null; if (searchScopes == null) { error = AntViewActionMessages.SearchForBuildFilesDialog_Must_select_a_working_set_10; } else if (searchScopes.isEmpty()) { error = AntViewActionMessages.SearchForBuildFilesDialog_No_searchable; } if (error != null) { setErrorMessage(error); getOkButton().setEnabled(false); return; } } getOkButton().setEnabled(true); setErrorMessage(null); } /** * Handles the working set choose button pressed. Returns the name of the chosen working set or <code>null</code> if none. */ private void handleChooseButtonPressed() { IWorkingSetSelectionDialog dialog = PlatformUI.getWorkbench().getWorkingSetManager().createWorkingSetSelectionDialog(getShell(), false); if (dialog.open() == Window.CANCEL) { return; } IWorkingSet[] sets = dialog.getSelection(); if (sets == null) { return; } if (sets.length == 0) { setWorkingSet(null); // ok pressed with no working set selected } else { setWorkingSet(sets[0]); // We disallowed multi-select } } /** * Sets the current working set search scope. This populates the search scope with resources found in the given working set and updates the * enabled state of the dialog based on the sets contents. * * @param set * the working set scope for the search */ private void setWorkingSet(IWorkingSet set) { if (set == null) { searchScopes = null; workingSetText.setText(IAntCoreConstants.EMPTY_STRING); validateInput(); return; } IAdaptable[] elements = set.getElements(); searchScopes = new ArrayList<>(); for (int i = 0; i < elements.length; i++) { // Try to get an IResource object from each element IResource resource = null; IAdaptable adaptable = elements[i]; if (adaptable instanceof IResource) { resource = (IResource) adaptable; } else { resource = adaptable.getAdapter(IResource.class); } if (resource != null) { searchScopes.add(resource); } } workingSetText.setText(set.getName()); selectRadioButton(workingSetScopeButton); validateInput(); } /** * Returns the trimmed user input */ private String getInput() { return getText().getText().trim(); } /** * Returns the search results */ public IFile[] getResults() { return results.toArray(new IFile[results.size()]); } /** * Returns whether the user wishes to include results which cannot be parsed. */ protected boolean getIncludeErrorResults() { return settings.getBoolean(IAntUIPreferenceConstants.ANTVIEW_INCLUDE_ERROR_SEARCH_RESULTS); } /** * When the user presses the search button (tied to the OK id), search the workspace for files matching the regular expression in the input field. */ @Override protected void okPressed() { String input = getInput(); settings.put(IAntUIPreferenceConstants.ANTVIEW_LAST_SEARCH_STRING, input); settings.put(IAntUIPreferenceConstants.ANTVIEW_INCLUDE_ERROR_SEARCH_RESULTS, includeErrorResultButton.getSelection()); settings.put(IAntUIPreferenceConstants.ANTVIEW_LAST_WORKINGSET_SEARCH_SCOPE, getWorkingSetName()); settings.put(IAntUIPreferenceConstants.ANTVIEW_USE_WORKINGSET_SEARCH_SCOPE, workingSetScopeButton.getSelection()); results = new ArrayList<>(); // Clear previous results ResourceProxyVisitor visitor = new ResourceProxyVisitor(); if (searchScopes == null || searchScopes.isEmpty()) { try { ResourcesPlugin.getWorkspace().getRoot().accept(visitor, IResource.NONE); } catch (CoreException ce) { // Closed project...don't want build files from there } } else { Iterator<IResource> iter = searchScopes.iterator(); while (iter.hasNext()) { try { iter.next().accept(visitor, IResource.NONE); } catch (CoreException ce) { // Closed project...don't want build files from there } } } super.okPressed(); } /** * Searches for files whose name matches the given regular expression. */ class ResourceProxyVisitor implements IResourceProxyVisitor { Pattern pattern; ResourceProxyVisitor() { // Users use "*" and "?" where regex uses ".*" and ".?" // The character "." must be escaped in regex String input = getInput(); // replace "." with "\\." input = input.replaceAll("\\.", "\\\\."); //$NON-NLS-1$ //$NON-NLS-2$ // replace "*" with ".*" input = input.replaceAll("\\*", "\\.\\*"); //$NON-NLS-1$ //$NON-NLS-2$ // replace "?" with ".?" input = input.replaceAll("\\?", "\\.\\?"); //$NON-NLS-1$ //$NON-NLS-2$ pattern = Pattern.compile(input); } /** * @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.core.resources.IResourceProxy) */ @Override public boolean visit(IResourceProxy proxy) { if (proxy.getType() == IResource.FILE) { Matcher matcher = pattern.matcher(proxy.getName()); if (matcher.find()) { results.add(proxy.requestResource()); } return false; } return true; } } /* * (non-Javadoc) * * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell) */ @Override protected void configureShell(Shell shell) { super.configureShell(shell); PlatformUI.getWorkbench().getHelpSystem().setHelp(shell, IAntUIHelpContextIds.SEARCH_FOR_BUILDFILES_DIALOG); } /* * (non-Javadoc) * * @see org.eclipse.jface.dialogs.InputDialog#validateInput() */ @Override protected void validateInput() { String errorMessage = null; if (getValidator() != null) { errorMessage = getValidator().isValid(getText().getText()); } setErrorMessage(errorMessage); if (errorMessage == null) { updateForWorkingSetSettings(); } } }